/*
 * Object representation of a task in BeautyRoxy system.
 * 
 */
// @author A0105514U
/** @author A0105514U */

package global;

import global.Constant;
import global.SystemFunction;
import global.Time;

public class Task implements Comparable<Task>{
	private final int TASK_STRING_LENGTH = 5;
	private final String TYPE_UNMATCHED_MESSAGE = "Task Construction Error: Task Type Unmatched\n %1$s";
	private final String COMPARE_ERROR_MESSAGE = "Comparison between float tasks and non-float tasks is prohibited.";
	private final int COMPARE_ERROE_RETURN_VALUE = -1;
	private final int COMPARE_GREATER_THAN_VALUE = 1;
	private final int COMPARE_LESS_THAN_VALUE = -1;
	private final int COMPARE_EQUAL_TO_VALUE = 0;
	private final String CLOCK_SEPARATOR = ":";
	
	private final String START_TIME_DECORATOR = " from ";
	private final String END_TIME_DECORATOR = " to ";
	private final String EXACT_TIME_DECORATOR = " at ";
	private final String DEADLINE_TIME_DECORATOR = " by ";
	private final String LOCATION_DECORATOR = " at ";
	private final String OPEN_BRACKET = " (";
	private final String CLOSE_BRACKET = ") ";
	private final String AM = "AM";
	private final String PM = "PM";
	
	private final String FLOAT_TASK_TYPE_STRING = "0";
	private final String TIME_TASK_TYPE_STRING = "1";
	private final String DEADLINE_TASK_TYPE_STRING = "2";
	private final String FLOAT_TASK_ENDTIME_STRING = "0";
	
	private final String TASK_FILE_EXTENSION = ".txt";
	
	private String taskType;
	private String taskName;
	private String taskLocation;
	private Time taskStartTime;
	private Time taskEndTime;
	private String taskId;
	private boolean isDone;
	private boolean isSync;
	
	/*  
	   Constructor for timed tasks WITH all task attributes available
	*/
	public Task(String taskType,String taskName,String taskLocation,
				Time taskStartTime,Time taskEndTime){
		initFields(taskType,taskName,taskLocation, taskStartTime,taskEndTime);
	}

	/* 
	     Constructor for deadline tasks
	 */
	public Task(String taskType,String taskName,String taskLocation,Time taskEndTime){
		this(taskType,taskName,taskLocation,Constant.EMPTY_TASK_STARTING_TIME,taskEndTime);
	}

	/*
		Constructor for float tasks without location
	*/
	public Task(String taskType,String taskName){
		initFields(taskType,taskName,Constant.EMPTY_TASK_LOCATION,Constant.EMPTY_TASK_STARTING_TIME,Constant.EMPTY_TASK_ENDING_TIME);
	}
	
	public Task(String taskType,String taskName,String location){
		initFields(taskType,taskName,location,Constant.EMPTY_TASK_STARTING_TIME,Constant.EMPTY_TASK_ENDING_TIME);
	}

	/*  Constructor using content string stored in the storage

		We assume the content of a task stored in the storage 
		would only be one of the two possible forms:
		    1. "taskType#taskName" for float tasks
		    2. "taskType#taskName#taskLocation#taskStartTime#taskEndTime"
		    	for time and deadline tasks
		@param taskString
					is the content string of the task
	*/
	public Task(String taskString){
		try{
			initFields(taskString);
		}catch(Exception e){
			
		}
	}
	
	private void initFields(String taskString) throws Exception{
		String[] taskAttributeList = taskString.trim().split(Constant.HEX);
		
		// check if it is a time task or a deadline task
		if (taskAttributeList.length == TASK_STRING_LENGTH){  
			String taskTypeString = taskAttributeList[0];

			if(taskTypeString.equalsIgnoreCase(Constant.TIME_TASK_TYPE) || 
					taskTypeString.equalsIgnoreCase(Constant.DEADLINE_TASK_TYPE) ||
					taskTypeString.equalsIgnoreCase(Constant.FLOAT_TASK_TYPE)){  // check if the type message matches
				
				// retrieving taskNameString, with special characters removed. 
				String taskNameString = taskAttributeList[1].replaceAll(Constant.SPECIALS_REGEX, Constant.EMPTY_STRING);
				
				// retrieving other attribute values
				String taskLocationString = taskAttributeList[2];
				String taskStartTimeString = taskAttributeList[3];
				String taskEndTimeString = taskAttributeList[4];
				Time taskStartTime = (taskStartTimeString.equalsIgnoreCase(Constant.NULL_STRING))? null : new Time(taskStartTimeString);
				Time taskEndTime = (taskEndTimeString.equalsIgnoreCase(Constant.NULL_STRING))? null : new Time(taskEndTimeString);
				
				// initializing task fields
				initFields(taskTypeString,taskNameString,taskLocationString,
							taskStartTime,taskEndTime);
			}else{
				throw new Exception(String.format(TYPE_UNMATCHED_MESSAGE,taskString));
			}
		}else{
			throw new Exception(String.format(TYPE_UNMATCHED_MESSAGE,taskString));
		}
	}
	
	private void initFields(String taskType,String taskName,String taskLocation,Time taskStartTime,Time taskEndTime){
		taskName = taskName.replaceAll(Constant.SPECIALS_REGEX, Constant.EMPTY_STRING);
		
		this.taskType = taskType;
		this.taskName = taskName;
		this.taskLocation = taskLocation;
		this.taskStartTime = taskStartTime;
		this.taskEndTime = taskEndTime;
		this.taskId = Constant.EMPTY_STRING;
		this.setUnSync();
		this.markUnDone();
	}
	
	public Time getStartTime(){
		return taskStartTime;
	}
	
	public Time getEndTime(){
		return taskEndTime;
	}
	
	public String getTaskType(){
		return taskType;
	}
	
	public String getTaskName(){
		return taskName;
	}
	
	public String getTaskLocation(){
		return taskLocation;
	}
	
	public void setStartTime(Time newStartTime){
		taskStartTime = newStartTime;
	}
	
	public void setEndTime(Time newEndTime){
		taskEndTime = newEndTime;
	}
	
	public void setTaskType(String newTaskType){
		taskType = newTaskType;
	}
	
	public void setTaskName(String newTaskName){
		taskName = newTaskName;
	}
	
	public void setTaskLocation(String newTaskLocation){
		taskLocation = newTaskLocation;
	}

	public String getTaskFileName(){
		String taskTypeString,taskEndTimeString;
		
		assert(taskType.equalsIgnoreCase(Constant.FLOAT_TASK_TYPE)
				|| taskType.equalsIgnoreCase(Constant.TIME_TASK_TYPE)
				|| taskType.equalsIgnoreCase(Constant.DEADLINE_TASK_TYPE));

		if(taskType.equalsIgnoreCase(Constant.FLOAT_TASK_TYPE)){
			taskTypeString = FLOAT_TASK_TYPE_STRING;
			taskEndTimeString = FLOAT_TASK_ENDTIME_STRING;
		}else if(taskType.equalsIgnoreCase(Constant.TIME_TASK_TYPE)){
			taskTypeString = TIME_TASK_TYPE_STRING;
			if(taskEndTime==null){
				taskEndTimeString = Constant.EMPTY_STRING;
			}else{
				taskEndTimeString = taskEndTime.toString();
			}
		}else{
			taskTypeString = DEADLINE_TASK_TYPE_STRING;
			if(taskEndTime==null){
				taskEndTimeString = Constant.EMPTY_STRING;
			}else{
				taskEndTimeString = taskEndTime.toString();
			}
		}
		
		String taskLocationString;
		
		if(taskLocation == null || taskLocation.equalsIgnoreCase(Constant.NULL_STRING)){
			taskLocationString = Constant.EMPTY_STRING;
		}else{
			taskLocationString = taskLocation;
		}

		String fileName = taskEndTimeString+taskName+taskLocationString+taskTypeString+TASK_FILE_EXTENSION;
		return fileName;
	}

	public String toString(){
		return taskType+Constant.HEX+taskName+Constant.HEX+taskLocation+Constant.HEX
				+taskStartTime+Constant.HEX+taskEndTime;
	}

	public String getFormattedTaskString(){
		String formattedTaskString = Constant.EMPTY_STRING;

		formattedTaskString += (taskName);

		if (taskLocation != null && !taskLocation.equals(Constant.EMPTY_STRING) && !taskLocation.equalsIgnoreCase(Constant.NULL_STRING)){
			formattedTaskString += (LOCATION_DECORATOR + taskLocation);
		}
		
		if(taskType.equalsIgnoreCase(Constant.TIME_TASK_TYPE) && taskStartTime != null){
			if(taskEndTime != null){
				formattedTaskString += (START_TIME_DECORATOR+taskStartTime.getFormattedTimeString());
				formattedTaskString += (END_TIME_DECORATOR+taskEndTime.getFormattedTimeString());
			}else{
				formattedTaskString += (EXACT_TIME_DECORATOR+taskStartTime.getFormattedTimeString());
			}
		}else{
			if(taskEndTime != null){
				formattedTaskString += (DEADLINE_TIME_DECORATOR+taskEndTime.getFormattedTimeString());
			}
		}
		
		return formattedTaskString;
	}
	
	/** @author A0105661M */
	public String getFormattedTaskStringNew(){
		String formattedTaskString = Constant.EMPTY_STRING;

		formattedTaskString += (taskType);
		
		if(taskType.equalsIgnoreCase(Constant.TIME_TASK_TYPE)){
			String startTimeString = taskStartTime.getFormattedTimeStringSingaporeFormat();
			String endTimeString = taskEndTime.getFormattedTimeStringSingaporeFormat();
			
			String dateStart = startTimeString.substring(0, 10) +OPEN_BRACKET+ convertDay(taskStartTime.getDay()) + CLOSE_BRACKET;
			String dateEnd = endTimeString.substring(0, 10) + OPEN_BRACKET + convertDay(taskEndTime.getDay()) + CLOSE_BRACKET;
			String timeStart = convertToAMPM(startTimeString.substring(11)) + Constant.SPACE;
			String timeEnd = convertToAMPM(endTimeString.substring(11)) + Constant.SPACE;
			
			if (dateStart.equals(dateEnd)){
				formattedTaskString += " oneDayEvent ";
				formattedTaskString += dateStart;
				formattedTaskString += (timeStart + " - " + timeEnd);
			} else {
				formattedTaskString += " multiDayEvent ";
				formattedTaskString += dateStart;
				formattedTaskString += timeStart;
				formattedTaskString += dateEnd;
				formattedTaskString += timeEnd;
			}
			
		}else{
			if(taskEndTime != null){
				String endTimeString = taskEndTime.getFormattedTimeStringSingaporeFormat();
				String dateEnd = endTimeString.substring(0, 10) + " (" + convertDay(taskEndTime.getDay()) + ") ";
				String timeEnd = convertToAMPM(endTimeString.substring(11)) + " ";
				formattedTaskString += (" "+ dateEnd + timeEnd + ": ");
			}
		}
		
		if(isDone){
			formattedTaskString += (" " + "(done)");
		}
		
		formattedTaskString += (" "+ taskName);

		if (taskLocation != null && !taskLocation.equals("") && !taskLocation.equalsIgnoreCase("null")){
			formattedTaskString += (" at " + taskLocation);
		}
		
		return formattedTaskString;
	}
	
	/** @author A0105661M */
	/*
	 * Suggestion:
	 * 	If possible, move this method out of Task class.
	 */
	private String convertDay(int day) {
		String result = "";
		
		switch (day){
		case 0:
			result = "Sat";
			break;
		case 1:
			result = "Sun";
			break;
		case 2:
			result = "Mon";
			break;
		case 3:
			result = "Tue";
			break;
		case 4:
			result = "Wed";
			break;
		case 5:
			result = "Thur";
			break;
		case 6:
			result = "Fri";
			break;
		default:
		}
		
		return result;
	}

	/** @author A0105661M */
	private String convertToAMPM(String timeString) {
		String hours = timeString.substring(0, 2);
		String minutes = timeString.substring(3);
		String type = Constant.EMPTY_STRING;
		int hoursInt = Methods.safeParseInt(hours) ;
		
		if (hoursInt< 12){
			type = AM;
		} else {
			type = PM;
			if (hoursInt > 12){
				hoursInt -= 12;
			} 
		}
		
		return (hoursInt + CLOCK_SEPARATOR + minutes + type);
	}

	/** @author A0105514U */
	public String getId(){
		return taskId;
	}
	
	public void setId(String id){
		this.taskId = id;
	}
	
	public void markDone(){
		isDone = true;
	}
	
	public void markUnDone(){
		isDone = false;
	}
	
	public boolean getTaskStatus(){
		return isDone;
	}
	
	public boolean getSyncStatus(){
		return isSync;
	}
	
	public void setSync(){
		isSync = true;
	}
	
	public void setUnSync(){
		isSync = false;
	}
	
	public void setTaskTypeAsNull(){
		taskType = null;
	}

	@Override
	public int compareTo(Task other) {
		String otherTaskType = other.getTaskType();
		String thisTaskType = this.getTaskType();

		try{
			// If both tasks are non-float tasks, we compare them using their end time
			if((otherTaskType.equalsIgnoreCase(Constant.TIME_TASK_TYPE)
				|| otherTaskType.equalsIgnoreCase(Constant.DEADLINE_TASK_TYPE))
			   && 
			   (thisTaskType.equalsIgnoreCase(Constant.TIME_TASK_TYPE) 
			    || thisTaskType.equalsIgnoreCase(Constant.DEADLINE_TASK_TYPE))){

				Time otherEndTime = other.getEndTime();
				if(this.taskEndTime.before(otherEndTime)){
					return COMPARE_GREATER_THAN_VALUE;
				}else if(this.taskEndTime.after(otherEndTime)){
					return COMPARE_LESS_THAN_VALUE;
				}else{
					return COMPARE_EQUAL_TO_VALUE;
				}
			}else if(thisTaskType.equalsIgnoreCase(Constant.FLOAT_TASK_TYPE) 
				  && otherTaskType.equalsIgnoreCase(Constant.FLOAT_TASK_TYPE)){
				String otherTaskName = other.getTaskName();
				return this.taskName.compareTo(otherTaskName);
			}else{
				throw new Exception(COMPARE_ERROR_MESSAGE);
			}
		}catch(Exception e){
			return COMPARE_ERROE_RETURN_VALUE;
		}
	}
}
